﻿using System;
using System.Data;
using System.Data.Entity.Infrastructure;
using System.Linq;
using System.Linq.Expressions;
using System.Collections.Generic;
using System.Data.Entity;
using System.Data.Entity.Validation;

namespace PMngr.Data.EF
{
    public class EFRepository : IRepository
    {
        private DbContext _context;

        public EFRepository(DbContext context)
        {
            _context = context;
        }

        public void CommitChanges()
        {
            var recordsAffected = _context.SaveChanges();
        }

        public void Delete<T>(Expression<Func<T, bool>> expression) where T : class
        {
            foreach (var item in _context.Set<T>().Where(expression))
                Delete(item);
        }

        public void Delete<T>(T item) where T : class
        {
            _context.Set<T>().Remove(item);
        }

        public void DeleteAll<T>() where T : class
        {
            foreach (var item in _context.Set<T>().AsQueryable())
                Delete(item);
        }

        public T Single<T>(Expression<Func<T, bool>> expression) where T : class
        {
            return _context.Set<T>().FirstOrDefault(expression);
        }

        public T Find<T>(params object[] keyValues) where T : class
        {
            return _context.Set<T>().Find(keyValues);
        }

        public IQueryable<T> Find<T>(Expression<Func<T,bool>> predicate) where T : class
        {
            return All<T>().Where(predicate);
        }

        public IQueryable<T> All<T>() where T : class
        {
            return _context.Set<T>().AsQueryable();
        }

        public void Add<T>(T item) where T : class
        {
            _context.Set<T>().Add(item);
        }

        public void Add<T>(IEnumerable<T> items) where T : class
        {
            foreach (var item in items)
                Add(item);
        }

        public void TrySave<T>() where T : class
        {
            try
            {
                //nothing needed?
                var affected = _context.SaveChanges();
            }
            catch (DbEntityValidationException ex)
            {
                var errors = new List<ErrorEntity>();
                foreach (var failure in ex.EntityValidationErrors)
                {
                    var error = new ErrorEntity {Type = failure.Entry.Entity.GetType()};

                    var es = failure.ValidationErrors.Select(e => new ErrorInfo {Property = e.PropertyName, Message = e.ErrorMessage}).ToList();
                    error.Errors = es;
                    errors.Add(error);
                }
                
                throw (new ValidationException(errors));
            }
            catch (DbUpdateException ex)
            {
                foreach (var entity in _context.ChangeTracker.Entries())
                {
                    entity.State = EntityState.Unchanged;
                }
               
                throw ex;
            }
        }

        public void Dispose()
        {
            _context.Dispose();
        }
    }
}
